#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include <devices/shutdown.h>
#include <string.h>
#include <filesys/file.h>
#include <devices/input.h>
#include <threads/malloc.h>
#include <threads/palloc.h>

#include "threads/interrupt.h"
#include "threads/thread.h"
#include "process.h"
#include "pagedir.h"d
#include <threads/vaddr.h>
#include <filesys/filesys.h>

# define max_syscall 20
# define USER_VADDR_BOUND (void*) 0x08048000
/* Restore the address of different kinds of system calls */
static void (*syscalls[max_syscall])(struct intr_frame *);

void sys_halt(struct intr_frame* f);    /* syscall halt. */
void sys_exit(struct intr_frame* f);    /* syscall exit. */
void sys_exec(struct intr_frame* f);    /* syscall exec. */
void sys_create(struct intr_frame* f);  /* syscall create */
void sys_remove(struct intr_frame* f);  /* syscall remove */
void sys_open(struct intr_frame* f);    /* syscall open */
void sys_wait(struct intr_frame* f);    /*syscall wait */
void sys_filesize(struct intr_frame* f);/* syscall filesize */
void sys_read(struct intr_frame* f);    /* syscall read */
void sys_write(struct intr_frame* f);   /* syscall write */
void sys_seek(struct intr_frame* f);    /* syscall seek */
void sys_tell(struct intr_frame* f);    /* syscall tell */
void sys_close(struct intr_frame* f);   /* syscall close */
struct thread_file * find_file_id(int fd);
static void syscall_handler (struct intr_frame *);


/* 系统调用初始化 */
void
syscall_init (void)
{
  intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
  syscalls[SYS_HALT] = &sys_halt;
  syscalls[SYS_EXIT] = &sys_exit;
  syscalls[SYS_EXEC] = &sys_exec;
  syscalls[SYS_WAIT] = &sys_wait;
  syscalls[SYS_CREATE] = &sys_create;
  syscalls[SYS_REMOVE] = &sys_remove;
  syscalls[SYS_OPEN] = &sys_open;
  syscalls[SYS_WRITE] = &sys_write;
  syscalls[SYS_SEEK] = &sys_seek;
  syscalls[SYS_TELL] = &sys_tell;
  syscalls[SYS_CLOSE] =&sys_close;
  syscalls[SYS_READ] = &sys_read;
  syscalls[SYS_FILESIZE] = &sys_filesize;
}

/* 使用文档中提供的方法进行验证
   Reads a byte at user virtual address UADDR.
   UADDR must be below PHYS_BASE.
   Returns the byte value if successful, -1 if a segfault
   occurred. */
static int
get_user (const uint8_t *uaddr)
{
  int result;
  asm ("movl $1f, %0; movzbl %1, %0; 1:"
       : "=&a" (result) : "m" (*uaddr));
  return result;
}

/* 检查指针是否合法 */
void * 
check_ptr(const void *vaddr)
{ 
  /* 检查地址是否为用户虚拟地址 addr */
  if (!is_user_vaddr(vaddr))
  {
    exit_special ();
  }
  /* 检查页 Page */
  void *ptr = pagedir_get_page (thread_current()->pagedir, vaddr);
  if (!ptr)
  {
    exit_special ();
  }
  /* 检查页 boundary 是否合法 */
  uint8_t *byte = (uint8_t *) vaddr;
  for (uint8_t i = 0; i < 4; i++) 
  {
    if (get_user(byte + i) == -1)
    {
      exit_special ();
    }
  }
  return ptr;
}

/* 实现sys_halt */
void 
sys_halt (struct intr_frame* f)
{
  shutdown_power_off();
}


/* 实现sys_exit */
void 
sys_exit (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  *user_ptr++;
  /* record the exit status of the process */
  thread_current()->ret = *user_ptr;
  thread_exit ();
}

/* 实现sys_exec */
void 
sys_exec (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  check_ptr (*(user_ptr + 1));
  *user_ptr++;
  f->eax = process_execute((char*)* user_ptr);
}

/* 实现sys_wait */
void 
sys_wait (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  *user_ptr++;
  f->eax = process_wait(*user_ptr);
}


/* 实现sys_create*/
void 
sys_create(struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  // check_ptr (user_ptr + 5);
  // check_ptr (*(user_ptr + 4));
  check_ptr (user_ptr + 2);
  check_ptr (*(user_ptr + 1));
  *user_ptr++;
  acquire_lock_f ();
  f->eax = filesys_create ((const char *)*user_ptr, *(user_ptr+1));
  release_lock_f ();
}

/* 实现sys_remove */
void 
sys_remove(struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  check_ptr (*(user_ptr + 1));
  *user_ptr++;
  acquire_lock_f ();
  f->eax = filesys_remove ((const char *)*user_ptr);
  release_lock_f ();
}

/* 实现sys_open */
void 
sys_open (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  check_ptr (*(user_ptr + 1));
  *user_ptr++;
  acquire_lock_f ();
  struct file * file_opened = filesys_open((const char *)*user_ptr);
  release_lock_f ();

  /* 对打开的文件进行维护 */
  struct thread * t = thread_current();
  if (file_opened)
  {
    struct thread_file *file_temp = malloc(sizeof(struct thread_file));
    file_temp->fd = t->file_fd++;
    file_temp->file = file_opened;
    list_push_back (&t->files, &file_temp->file_elem);
    f->eax = file_temp->fd;
  } 
  else
  {
    f->eax = -1;
  }
}
/* 实现sys_write */
void 
sys_write (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;

  check_ptr (user_ptr + 3);

  *user_ptr++;
  int fd = *user_ptr;
  const char * buffer = (const char *)*(user_ptr+1);
  off_t size = *(user_ptr+2);
  check_ptr (buffer);
  check_ptr (buffer + size - 1);
  // int i;
  // for (i = 0; i < size; i++)
  //   check_ptr(buffer + i);
  if (fd == 1) {
    /* 标准输出，使用putbuf */
    putbuf(buffer,size);
    f->eax = size;
  }
  else
  {
    /* 写入文件 */
    struct thread_file * file_temp = find_file_id (fd);
    if (file_temp)
    {
      acquire_lock_f ();
      f->eax = file_write (file_temp->file, buffer, size);
      release_lock_f ();
    } 
    else
    {
      f->eax = 0;
    }
  }
}
/* 实现sys_seek */
void 
sys_seek(struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  // check_ptr (user_ptr + 5);
  check_ptr (user_ptr + 2);
  *user_ptr++;
  struct thread_file *file_temp = find_file_id (*user_ptr);
  if (file_temp)
  {
    acquire_lock_f ();
    file_seek (file_temp->file, *(user_ptr+1));
    release_lock_f ();
  }
}

/* 实现sys_tell */
void 
sys_tell (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  *user_ptr++;
  struct thread_file *file_temp = find_file_id (*user_ptr);
  if (file_temp)
  {
    acquire_lock_f ();
    f->eax = file_tell (file_temp->file);
    release_lock_f ();
  }
  else
  {
    f->eax = -1;
  }
}

/* 实现sys_close*/
void 
sys_close (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  *user_ptr++;
  struct thread_file * opened_file = find_file_id (*user_ptr);
  if (opened_file)
  {
    acquire_lock_f ();
    file_close (opened_file->file);
    release_lock_f ();

    list_remove (&opened_file->file_elem);
    free (opened_file);
  }
}
/* 实现sys_filesize */
void 
sys_filesize (struct intr_frame* f){
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 1);
  *user_ptr++;
  struct thread_file * file_temp = find_file_id (*user_ptr);
  if (file_temp)
  {
    acquire_lock_f ();
    f->eax = file_length (file_temp->file);
    release_lock_f ();
  } 
  else
  {
    f->eax = -1;
  }
}



/* 实现sys_read */
void 
sys_read (struct intr_frame* f)
{
  uint32_t *user_ptr = f->esp;
  check_ptr (user_ptr + 3);
  *user_ptr++;

  int i;
  int fd = *user_ptr;
  uint8_t * buffer = (uint8_t*)*(user_ptr+1);
  off_t size = *(user_ptr+2);

  check_ptr (buffer);
  check_ptr (buffer + size - 1);

  if (fd == 0) 
  {
    for (i = 0; i < size; i++)
      buffer[i] = input_getc();
    f->eax = size;
  }
  else
  {
    struct thread_file * file_temp = find_file_id (*user_ptr);
    if (file_temp)
    {
      acquire_lock_f ();
      f->eax = file_read (file_temp->file, buffer, size);
      release_lock_f ();
    } 
    else
    {
      f->eax = -1;
    }
  }
}

/* 指正错误，退出线程，释放资源*/
void 
exit_special (void)
{
  thread_current()->ret = -1;
  thread_exit ();
}

/* 通过文件id找到file */
struct thread_file * 
find_file_id (int file_id)
{
  struct list_elem *e;
  struct thread_file * file_temp = NULL;
  struct list *files = &thread_current ()->files;
  for (e = list_begin (files); e != list_end (files); e = list_next (e)){
    file_temp = list_entry (e, struct thread_file, file_elem);
    if (file_id == file_temp->fd)
      return file_temp;
  }
  return false;
}

/* 处理系统调用 */
static void
syscall_handler (struct intr_frame *f UNUSED)
{
  /* 检查栈是否合法 */
  check_ptr (f->esp);
  int type = * (int *)f->esp;
  if(type <= 0 || type >= max_syscall){
    exit_special ();
  }
  syscalls[type](f);
}
